Створюйте плавні веб-досвіди, як у додатках. Цей посібник розкриває потужність псевдоелементів CSS View Transition для стилізації динамічних переходів з прикладами.
Опановуємо CSS View Transitions: Глибоке занурення у стилізацію псевдоелементів
У світі веб-розробки, що постійно змінюється, прагнення до безшовного, плавного та захоплюючого користувацького досвіду є незмінним. Роками розробники намагалися подолати розрив між вебом та нативними додатками, особливо щодо плавності переходів сторінок. Традиційна веб-навігація часто призводить до різкого перезавантаження всієї сторінки — порожнього білого екрану, що на мить руйнує занурення користувача. Односторінкові додатки (SPA) частково вирішили цю проблему, але створення кастомних, змістовних переходів залишалося складним і часто ненадійним завданням, що значною мірою залежало від JavaScript-бібліотек та складного управління станом.
І тут з'являється CSS View Transitions API — революційна технологія, що має на меті змінити наш підхід до обробки змін в UI у вебі. Цей потужний API надає простий, але неймовірно гнучкий механізм для анімації між різними станами DOM, роблячи створення витончених, схожих на додатки досвідів, яких очікують користувачі, простішим, ніж будь-коли. В основі потужності цього API лежить набір нових CSS-псевдоелементів. Це не звичайні селектори; це динамічні, тимчасові елементи, що генеруються браузером, щоб надати вам детальний контроль над кожною фазою переходу. Цей посібник проведе вас у глибоке занурення в це дерево псевдоелементів, досліджуючи, як стилізувати кожен компонент для створення приголомшливих, продуктивних та доступних анімацій для глобальної аудиторії.
Анатомія View Transition
Перш ніж ми зможемо стилізувати перехід, ми повинні зрозуміти, що відбувається «під капотом», коли він запускається. Коли ви ініціюєте перехід (наприклад, викликаючи document.startViewTransition()), браузер виконує низку кроків:
- Захоплення старого стану: Браузер робить «знімок екрана» поточного стану сторінки.
- Оновлення DOM: Ваш код вносить зміни в DOM (наприклад, переходить до нового представлення, додає або видаляє елементи).
- Захоплення нового стану: Після завершення оновлення DOM браузер робить знімок екрана нового стану.
- Побудова дерева псевдоелементів: Потім браузер створює тимчасове дерево псевдоелементів у накладанні сторінки. Це дерево містить захоплені зображення старого та нового станів.
- Анімація: CSS-анімації застосовуються до цих псевдоелементів, створюючи плавний перехід від старого стану до нового. За замовчуванням це просте перехресне згасання (cross-fade).
- Очищення: Після завершення анімації дерево псевдоелементів видаляється, і користувач може взаємодіяти з новим, живим DOM.
Ключ до кастомізації — це тимчасове дерево псевдоелементів. Уявіть його як набір шарів у графічному редакторі, тимчасово розміщених поверх вашої сторінки. Ви маєте повний контроль над цими шарами за допомогою CSS. Ось структура, з якою ви будете працювати:
- ::view-transition
- ::view-transition-group(*)
- ::view-transition-image-pair(*)
- ::view-transition-old(*)
- ::view-transition-new(*)
- ::view-transition-image-pair(*)
- ::view-transition-group(*)
Давайте розберемо, що представляє кожен з цих псевдоелементів.
Дійові особи: Псевдоелементи
::view-transition: Це корінь всієї структури. Це єдиний елемент, що заповнює вьюпорт і розташовується поверх усього іншого контенту сторінки. Він діє як контейнер для всіх груп, що переходять, і є чудовим місцем для встановлення загальних властивостей переходу, таких як тривалість або функція часу.
::view-transition-group(*): Для кожного окремого елемента, що переходить (ідентифікованого за CSS-властивістю view-transition-name), створюється група. Цей псевдоелемент відповідає за анімацію позиції та розміру свого вмісту. Якщо у вас є картка, що переміщується з одного боку екрана на інший, то насправді рухається саме ::view-transition-group.
::view-transition-image-pair(*): Вкладений у групу, цей елемент діє як контейнер та обрізаюча маска для старого та нового представлень. Його основна роль — підтримувати ефекти, такі як border-radius або transform, під час анімації та керувати стандартною анімацією перехресного згасання.
::view-transition-old(*): Це «знімок екрана» або відрендерене представлення елемента в його старому стані (до зміни DOM). За замовчуванням він анімується від opacity: 1 до opacity: 0.
::view-transition-new(*): Це «знімок екрана» або відрендерене представлення елемента в його новому стані (після зміни DOM). За замовчуванням він анімується від opacity: 0 до opacity: 1.
Корінь: Стилізація псевдоелемента ::view-transition
Псевдоелемент ::view-transition — це полотно, на якому малюється вся ваша анімація. Як контейнер верхнього рівня, це ідеальне місце для визначення властивостей, які повинні застосовуватися до переходу глобально. За замовчуванням браузер надає набір анімацій, але ви можете легко їх перевизначити.
Наприклад, стандартний перехід — це перехресне згасання, що триває 250 мілісекунд. Якщо ви хочете змінити це для кожного переходу на вашому сайті, ви можете націлитися на кореневий псевдоелемент:
::view-transition {
animation-duration: 500ms;
animation-timing-function: ease-in-out;
}
Це просте правило тепер змушує всі стандартні згасання сторінок тривати вдвічі довше і використовувати криву 'ease-in-out', надаючи їм дещо іншого відчуття. Хоча ви можете застосовувати тут складні анімації, зазвичай це місце краще використовувати для визначення універсального часу та функції пом'якшення, дозволяючи більш специфічним псевдоелементам займатися детальною хореографією.
Групування та іменування: Сила `view-transition-name`
«З коробки», без додаткових зусиль, View Transition API забезпечує перехресне згасання для всієї сторінки. Це обробляється однією групою псевдоелементів для кореня. Справжня сила API розкривається, коли ви хочете анімувати перехід конкретних, окремих елементів між станами. Наприклад, змусити мініатюру продукту на сторінці списку плавно збільшуватися та переміщатися на позицію основного зображення продукту на сторінці деталей.
Щоб повідомити браузеру, що два елементи в різних станах DOM концептуально є однаковими, ви використовуєте CSS-властивість view-transition-name. Ця властивість повинна бути застосована як до початкового, так і до кінцевого елемента.
/* У CSS сторінки списку */
.product-thumbnail {
view-transition-name: product-image;
}
/* У CSS сторінки деталей */
.main-product-image {
view-transition-name: product-image;
}
Надавши обом елементам однакову унікальну назву ('product-image' у цьому випадку), ви інструктуєте браузер: «Замість того, щоб просто згасати стару сторінку і з'являтися новій, створи спеціальний перехід для цього конкретного елемента». Тепер браузер згенерує спеціальну ::view-transition-group(product-image) для обробки його анімації окремо від кореневого згасання. Це фундаментальна концепція, яка уможливлює популярний ефект переходу «морфінг» або «спільний елемент».
Важливе зауваження: У будь-який момент під час переходу view-transition-name має бути унікальним. Ви не можете мати два видимих елементи з однаковою назвою одночасно.
Поглиблена стилізація: Основні псевдоелементи
Коли наші елементи названі, ми можемо заглибитися у стилізацію конкретних псевдоелементів, які браузер генерує для них. Саме тут ви можете створювати справді кастомні та виразні анімації.
`::view-transition-group(name)`: Рушій
Єдина відповідальність групи — перехід від розміру та позиції старого елемента до розміру та позиції нового. Вона не містить візуального вигляду контенту, а лише його обмежувальну рамку. Уявіть її як рухому раму.
За замовчуванням браузер анімує її властивості transform та width/height. Ви можете перевизначити це для створення різних ефектів. Наприклад, ви можете додати дугу до її руху, анімуючи її по кривій траєкторії, або змусити її збільшуватися та зменшуватися під час руху.
::view-transition-group(product-image) {
animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
У цьому прикладі ми застосовуємо специфічну функцію пом'якшення лише до руху зображення продукту, роблячи його більш динамічним та фізичним, не впливаючи на стандартне згасання решти сторінки.
`::view-transition-image-pair(name)`: Обрізувач та затемнювач
Вкладена в рухому групу, пара зображень містить старе та нове представлення. Вона діє як обрізаюча маска, тому якщо ваш елемент має border-radius, пара зображень гарантує, що контент залишатиметься обрізаним цим радіусом протягом всієї анімації розміру та позиції. Інше її головне завдання — керувати стандартним перехресним згасанням між старим та новим контентом.
Ви можете стилізувати цей елемент для забезпечення візуальної узгодженості або для створення більш просунутих ефектів. Ключовою властивістю, яку варто розглянути, є isolation: isolate. Це критично важливо, якщо ви плануєте використовувати просунуті ефекти mix-blend-mode на дочірніх елементах (старому та новому), оскільки це створює новий контекст накладання та запобігає впливу змішування на елементи поза групою переходу.
::view-transition-image-pair(product-image) {
isolation: isolate;
}
`::view-transition-old(name)` та `::view-transition-new(name)`: Зірки шоу
Це псевдоелементи, які представляють візуальний вигляд вашого елемента до та після зміни DOM. Саме тут відбуватиметься більша частина вашої роботи над кастомною анімацією. За замовчуванням браузер виконує на них просту анімацію перехресного згасання, використовуючи opacity та mix-blend-mode. Щоб створити власну анімацію, ви повинні спочатку вимкнути стандартну.
::view-transition-old(name),
::view-transition-new(name) {
animation: none;
}
Після вимкнення стандартної анімації ви можете застосовувати свою власну. Давайте розглянемо кілька поширених патернів.
Кастомна анімація: Ковзання
Замість перехресного згасання, давайте змусимо вміст контейнера в'їжджати. Наприклад, при навігації між статтями ми хочемо, щоб текст нової статті в'їжджав справа, а текст старої виїжджав вліво.
Спочатку визначимо ключові кадри:
@keyframes slide-from-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
@keyframes slide-to-left {
from { transform: translateX(0); }
to { transform: translateX(-100%); }
}
Тепер застосуємо ці анімації до старого та нового псевдоелементів для іменованого елемента 'article-content'.
::view-transition-old(article-content) {
animation: 300ms ease-out forwards slide-to-left;
}
::view-transition-new(article-content) {
animation: 300ms ease-out forwards slide-from-right;
}
Кастомна анімація: 3D-перевертання
Для більш драматичного ефекту ви можете створити 3D-перевертання картки. Це вимагає анімації властивості transform за допомогою rotateY, а також управління backface-visibility.
/* Групі потрібен 3D-контекст */
::view-transition-group(card-flipper) {
transform-style: preserve-3d;
}
/* Пара зображень також повинна зберігати 3D-контекст */
::view-transition-image-pair(card-flipper) {
transform-style: preserve-3d;
}
/* Старе представлення перевертається з 0 до -180 градусів */
::view-transition-old(card-flipper) {
animation: 600ms ease-in forwards flip-out;
backface-visibility: hidden;
}
/* Нове представлення перевертається з 180 до 0 градусів */
::view-transition-new(card-flipper) {
animation: 600ms ease-out forwards flip-in;
backface-visibility: hidden;
}
@keyframes flip-out {
from { transform: rotateY(0deg); }
to { transform: rotateY(-180deg); }
}
@keyframes flip-in {
from { transform: rotateY(180deg); }
to { transform: rotateY(0deg); }
}
Практичні приклади та просунуті техніки
Теорія корисна, але справжнє навчання відбувається на практиці. Давайте розглянемо деякі поширені сценарії та способи їх вирішення за допомогою псевдоелементів view transition.
Приклад: «Морфінг» мініатюри картки
Це класичний перехід зі спільним елементом. Уявіть галерею профілів користувачів. Кожен профіль — це картка з аватаром. Коли ви натискаєте на картку, ви переходите на сторінку деталей, де той самий аватар відображається на видному місці вгорі.
Крок 1: Призначте імена
На сторінці галереї зображення аватара отримує ім'я. Ім'я має бути унікальним для кожної картки, наприклад, на основі ID користувача.
/* У gallery-item.css */
.card-avatar { view-transition-name: avatar-user-123; }
На сторінці деталей профілю великий аватар у заголовку отримує точно таке ж ім'я.
/* У profile-page.css */
.profile-header-avatar { view-transition-name: avatar-user-123; }
Крок 2: Налаштуйте анімацію
За замовчуванням браузер перемістить та масштабує аватар, але він також застосує перехресне згасання до вмісту. Якщо зображення ідентичне, це згасання є зайвим і може викликати легке мерехтіння. Ми можемо його вимкнути.
/* Зірочка (*) тут є символом-замінником для будь-якої іменованої групи */
::view-transition-image-pair(*) {
/* Вимкнути стандартне згасання */
animation-duration: 0s;
}
Зачекайте, якщо ми вимкнемо згасання, як зміниться вміст? Для спільних елементів, де старе та нове представлення ідентичні, браузер достатньо розумний, щоб використовувати лише одне представлення для всього переходу. `image-pair` по суті містить лише одне зображення, тому вимкнення згасання просто розкриває цю оптимізацію. Для елементів, де вміст дійсно змінюється, вам знадобиться власна анімація замість стандартного згасання.
Обробка змін співвідношення сторін
Поширена проблема виникає, коли елемент, що переходить, змінює своє співвідношення сторін. Наприклад, ландшафтна мініатюра 16:9 на сторінці списку може переходити у квадратний аватар 1:1 на сторінці деталей. Стандартна поведінка браузера — анімувати ширину та висоту незалежно, що призводить до того, що зображення виглядає стиснутим або розтягнутим під час переходу.
Рішення елегантне. Ми дозволяємо ::view-transition-group обробляти зміну розміру та позиції, але ми перевизначаємо стилізацію старого та нового зображень всередині нього.
Мета полягає в тому, щоб старі та нові «знімки екрана» заповнювали свій контейнер без спотворення. Ми можемо зробити це, встановивши їхню ширину та висоту на 100% і дозволивши стандартній властивості object-fit браузера (яка успадковується від вихідного елемента) правильно обробляти масштабування.
::view-transition-old(hero-image),
::view-transition-new(hero-image) {
/* Запобігаємо спотворенню, заповнюючи контейнер */
width: 100%;
height: 100%;
/* Перевизначаємо стандартне перехресне згасання, щоб чітко бачити ефект */
animation: none;
}
З цим CSS `image-pair` плавно анімуватиме своє співвідношення сторін, а зображення всередині будуть правильно обрізані (залежно від їхнього значення `object-fit`), так само як вони були б у звичайному контейнері. Потім ви можете додати свої власні анімації, наприклад, перехресне згасання, поверх цієї виправленої геометрії.
Налагодження та підтримка браузерами
Стилізація елементів, які існують лише долю секунди, може бути складною. На щастя, сучасні браузери надають для цього чудові інструменти розробника. У Chrome або Edge DevTools ви можете перейти на панель «Animations», і коли ви запускаєте view transition, ви можете його призупинити. З призупиненою анімацією ви можете використовувати панель «Elements» для інспектування всього дерева псевдоелементів `::view-transition` так само, як і будь-якої іншої частини DOM. Ви можете бачити застосовані стилі та навіть змінювати їх у реальному часі, щоб вдосконалити свої анімації.
Станом на кінець 2023 року View Transitions API підтримується в браузерах на основі Chromium (Chrome, Edge, Opera). Реалізації знаходяться в процесі розробки для Firefox та Safari. Це робить його ідеальним кандидатом для прогресивного покращення. Користувачі з підтримуваними браузерами отримують чудовий, покращений досвід, тоді як користувачі інших браузерів отримують стандартну, миттєву навігацію. Ви можете перевірити підтримку в CSS:
@supports (view-transition: none) {
/* Всі стилі view-transition розміщуються тут */
::view-transition-old(my-element) { ... }
}
Найкращі практики для глобальної аудиторії
При реалізації анімацій важливо враховувати різноманітність користувачів та пристроїв у всьому світі.
Продуктивність: Анімації повинні бути швидкими та плавними. Дотримуйтеся анімації CSS-властивостей, які є «дешевими» для обробки браузером, насамперед transform та opacity. Анімація таких властивостей, як width, height або margin, може викликати перерахунок макета на кожному кадрі, що призводить до затримок та поганого досвіду, особливо на менш потужних пристроях.
Доступність: Деякі користувачі відчувають морську хворобу або дискомфорт від анімацій. Усі основні операційні системи надають користувачам можливість зменшити рух. Ми повинні поважати це. Медіазапит prefers-reduced-motion дозволяє вимкнути або спростити ваші анімації для цих користувачів.
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
/* Пропустити всі власні анімації та використовувати швидке, просте згасання */
animation: none !important;
}
}
Користувацький досвід (UX): Хороші переходи є цілеспрямованими. Вони повинні направляти увагу користувача та надавати контекст про зміни, що відбуваються в UI. Анімація, яка є занадто повільною, може змусити додаток відчуватися млявим, тоді як занадто яскрава може відволікати. Намагайтеся, щоб тривалість переходів була в діапазоні від 200 до 500 мс. Мета полягає в тому, щоб анімація більше відчувалася, ніж бачилася.
Висновок: Майбутнє за плавністю
CSS View Transitions API, і зокрема його потужне дерево псевдоелементів, є монументальним кроком уперед для веб-інтерфейсів. Він надає розробникам нативний, продуктивний та висококастомізований інструментарій для створення таких плавних, контекстних переходів, які колись були ексклюзивною прерогативою нативних додатків. Розуміючи ролі ::view-transition, ::view-transition-group та пар зображень old/new, ви можете вийти за рамки простих згасань та створювати складні, змістовні анімації, що покращують юзабіліті та захоплюють користувачів.
З розширенням підтримки браузерами цей API стане невід'ємною частиною інструментарію сучасного фронтенд-розробника. Приймаючи його можливості та дотримуючись найкращих практик щодо продуктивності та доступності, ми можемо побудувати веб, який є не тільки більш функціональним, але й красивішим та інтуїтивно зрозумілішим для всіх і всюди.